Libraries

Load required libraries

library(tidyverse)
library(here)
library(brms)
library(tidybayes)
library(sf)
library(mgcv)
library(priorsense)
library(osmdata)
library(nngeo)
library(glue)
library(arrow)

Import data

Import the modelling dataset - cleaned and prepped in the 2025-05-12_aq_clean.Rmd script


aq_model_data <- read_rds("input_data/aq_model_data.rds")

#also load the scale clusters
load("input_data/scale_72_clusters.rda") #SCALE clusters

#and the grid data
mw_100m_cropped <- read_rds("input_data/mw_100m_cropped.rds")
mw_100m_grid_sf <- read_rds("input_data/mw_100m_grid_sf.rds")

Functions

get_st_predictions(): A function to take posterior draws for a prediction matrix, write to an arrow database (to handle exhausting memory), then summarise


get_st_predictions <- function(model, nd, model_id, out_dir, ndraws = 1000, chunk_size = 10000, who_pm25_limit = 25) {
  # Create output folder
  dir.create(out_dir, showWarnings = FALSE, recursive = TRUE)

  nd <- nd %>% mutate(.row = row_number())
  n_chunks <- ceiling(nrow(nd) / chunk_size)

  # Save design matrix to disk for later joins
  nd_file <- file.path(out_dir, paste0("nd_", model_id, ".parquet"))
  arrow::write_parquet(nd, nd_file)

  # Loop over chunks
  for (i in seq_len(n_chunks)) {
    cat("Processing chunk", i, "of", n_chunks, "\n")

    idx <- ((i - 1) * chunk_size + 1):min(i * chunk_size, nrow(nd))
    nd_chunk <- nd[idx, ]

    epred_array <- posterior_epred(model, newdata = nd_chunk, ndraws = ndraws)

    epred_df <- as.data.frame.table(epred_array, responseName = "epred")
    colnames(epred_df) <- c("draw", "row_in_chunk", "epred")

    epred_df <- epred_df %>%
      mutate(
        draw = as.integer(draw),
        row_in_chunk = as.integer(row_in_chunk),
        .row = idx[row_in_chunk],
        model_id = model_id
      ) %>%
      select(draw, .row, epred, model_id)

    arrow::write_parquet(
      epred_df,
      sink = file.path(out_dir, glue::glue("epred_chunk_{i}.parquet"))
    )

    rm(epred_array, epred_df, nd_chunk)
    gc()
  }

  # Read from disk
  ds <- arrow::open_dataset(out_dir)
  nd_ds <- arrow::open_dataset(nd_file)

  summary_df <- ds %>%
    mutate(epred_pm25 = exp(epred)) %>%
    group_by(.row) %>%
    summarise(
      mean_log_epred = mean(epred, na.rm = TRUE),
      sd_log_epred = sd(epred, na.rm = TRUE),
      lwr_log = quantile(epred, 0.025, na.rm = TRUE),
      upr_log = quantile(epred, 0.975, na.rm = TRUE),
      
      mean_epred = mean(epred_pm25, na.rm = TRUE),
      sd_epred = sd(epred_pm25, na.rm = TRUE),
      lwr = quantile(epred_pm25, 0.025, na.rm = TRUE),
      upr = quantile(epred_pm25, 0.975, na.rm = TRUE),
      
      prob_exceed = mean(epred_pm25 > who_pm25_limit, na.rm = TRUE),
      .groups = "drop"
    ) %>%
    collect() %>%
    left_join(nd, by = ".row") %>%
    mutate(date = lubridate::month(date, label = TRUE))

  return(summary_df)
}

plot_st_predictions(): A function to plot spatiotemporal predictions from model posteriors


plot_st_predictions <- function(summary_df, 
                                cluster_sf,
                                value_limits = list(mean = NULL, sd = NULL, prob = c(0, 1))) {
  require(ggplot2)
  require(viridis)
  require(ggdist)
  require(dplyr)
  require(lubridate)

  # Mean prediction plotss
    p1 <- summary_df %>%
    ggplot() +
    geom_tile(aes(x = x, y = y, fill = mean_log_epred)) +
    geom_sf(data = cluster_sf, colour = "grey78", fill = NA) +
    scale_fill_viridis_c(option = "D", limits = value_limits$mean) +
    facet_wrap(~date) +
    labs(title = "Posterior mean of log(PM2.5) (µg/m³)", x = "", y = "") +
    theme_ggdist() +
    theme(panel.background = element_rect(fill = NA, colour = "grey78"),
          axis.text.x = element_text(angle = 45, hjust = 1),
          strip.background = element_rect(colour = "grey78"))
    
  p2 <- summary_df %>%
    ggplot() +
    geom_tile(aes(x = x, y = y, fill = mean_epred)) +
    geom_sf(data = cluster_sf, colour = "grey78", fill = NA) +
    scale_fill_viridis_c(option = "D", limits = value_limits$mean) +
    facet_wrap(~date) +
    labs(title = "Posterior mean of PM2.5 (µg/m³)", x = "", y = "") +
    theme_ggdist() +
    theme(panel.background = element_rect(fill = NA, colour = "grey78"),
          axis.text.x = element_text(angle = 45, hjust = 1),
          strip.background = element_rect(colour = "grey78"))

  # Standard deviation plot
  p3 <- summary_df %>%
    ggplot() +
    geom_tile(aes(x = x, y = y, fill = sd_epred)) +
    geom_sf(data = cluster_sf, colour = "grey78", fill = NA) +
    scale_fill_viridis_c(option = "F", limits = value_limits$sd) +
    facet_wrap(~date) +
    labs(title = "Posterior SD of PM2.5 (µg/m³)", x = "", y = "") +
    theme_ggdist() +
    theme(panel.background = element_rect(fill = NA, colour = "grey78"),
          axis.text.x = element_text(angle = 45, hjust = 1),
          strip.background = element_rect(colour = "grey78"))

  # Exceedance probability plot
  p4 <- summary_df %>%
    ggplot() +
    geom_tile(aes(x = x, y = y, fill = prob_exceed)) +
    geom_sf(data = cluster_sf, colour = "grey78", fill = NA) +
    scale_fill_viridis_c(option = "C", labels = scales::percent_format(), limits = value_limits$prob) +
    facet_wrap(~date) +
    labs(title = "Pr(PM2.5 > 25 µg/m³)", x = "", y = "") +
    theme_ggdist() +
    theme(panel.background = element_rect(fill = NA, colour = "grey78"),
          axis.text.x = element_text(angle = 45, hjust = 1),
          strip.background = element_rect(colour = "grey78"))

  list(mean_log_plot = p1, mean_plot = p2, sd_plot = p3, exceed_plot = p4)
}

compute_exposure_metrics() calculates exposure metrics for the 1 year p

PM2.5

First we will model PM2.5

Model 1: Day of year only

“We fitted a spatially smooth regression model for log-transformed PM2.5 concentrations using a Gaussian process (GP) smooth over spatial coordinates (x, y) to capture flexible spatial trends. Seasonal variation was modelled using a Fourier series expansion of day-of-year up to the 3rd harmonic (i.e., sine and cosine terms for annual, semi-annual, and tri-annual cycles) to account for complex seasonal patterns. The model was fitted in a Bayesian framework using brms with a Gaussian likelihood and the cmdstanr backend.”


priors <- c(
  prior(normal(3.4, 1), class = "Intercept"),
  prior(normal(0,1), class = "b"),
  prior(exponential(1), class = "sigma"),
  prior(exponential(1), class = "sdgp"),
  prior(normal(0, 1), class = "lscale", lb = 0)
)

Fit prior only model


m1_prior <- brm(
    formula = log_pm2_5 ~ gp(x, y, k=15) +
      sin_doy1 + cos_doy1 +
      sin_doy2 + cos_doy2 +
      sin_doy3 + cos_doy3,
    data = aq_model_data,
    family = gaussian(),
    prior = priors,
    sample_prior = "only",
    chains = 4, cores = 4,
    backend = "cmdstanr"
  )
G2;H2;Warningh: The global prior 'normal(0, 1)' of class 'lscale' will not be used in the model as all related coefficients have individual priors already. If you did not set those priors yourself, then maybe brms has assigned default priors. See ?set_prior and ?default_prior for more details.g
G3;Model executable is up to date!
gG3;Start sampling
g
Running MCMC with 4 parallel chains...

Chain 1 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 1 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 1 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 1 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 1 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 1 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 1 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 2 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 2 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 2 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 2 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 1 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 1 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 1 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 1 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 1 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 1 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 1 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 1 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 1 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 1 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 1 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 1 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 2 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 2 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 2 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 2 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 2 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 2 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 2 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 2 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 2 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 2 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 2 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 2 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 2 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 3 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 3 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 3 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 3 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 3 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 3 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 3 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 3 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 3 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 3 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 3 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 3 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 3 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 3 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 3 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 4 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 4 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 4 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 4 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 4 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 4 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 4 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 4 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 4 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 4 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 4 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 4 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 4 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 1 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 1 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 1 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 2 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 2 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 2 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 2 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 2 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 3 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 3 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 3 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 3 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 3 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 3 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 3 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 4 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 4 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 4 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 4 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 4 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 4 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 4 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 4 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 4 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 1 finished in 0.2 seconds.
Chain 2 finished in 0.2 seconds.
Chain 3 finished in 0.2 seconds.
Chain 4 finished in 0.2 seconds.

All 4 chains finished successfully.
Mean chain execution time: 0.2 seconds.
Total execution time: 0.4 seconds.
# summary(m1_prior)
# plot(m1_prior)

Now model with data.

m1 <- brm(
    formula = log_pm2_5 ~ gp(x, y, k=15)  +
    sin_doy1 + cos_doy1 +
    sin_doy2 + cos_doy2 +
    sin_doy3 + cos_doy3,
    data = aq_model_data,
    family = gaussian(),
    prior = priors,
    chains = 4, cores = 4,
    backend = "cmdstanr"
  )
G2;H2;Warningh: The global prior 'normal(0, 1)' of class 'lscale' will not be used in the model as all related coefficients have individual priors already. If you did not set those priors yourself, then maybe brms has assigned default priors. See ?set_prior and ?default_prior for more details.g
G3;Model executable is up to date!
gG3;Start sampling
g
Running MCMC with 4 parallel chains...

Chain 1 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 2 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 3 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 4 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 4 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 3 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 1 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 2 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 2 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 4 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 3 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 1 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 4 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 2 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 3 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 1 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 4 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 1 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 2 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 3 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 4 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 2 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 3 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 1 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 4 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 2 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 3 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 1 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 4 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 3 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 2 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 1 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 4 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 3 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 2 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 1 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 4 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 3 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 2 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 1 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 4 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 4 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 3 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 3 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 2 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 2 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 1 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 1 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 4 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 1 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 3 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 2 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 1 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 4 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 1 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 4 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 3 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 2 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 1 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 4 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 1 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 3 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 4 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 2 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 1 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 4 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 1 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 3 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 2 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 4 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 1 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 3 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 1 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 4 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 2 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 1 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 1 finished in 92.8 seconds.
Chain 4 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 3 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 2 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 4 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 4 finished in 98.1 seconds.
Chain 3 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 2 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 3 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 2 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 3 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 2 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 3 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 3 finished in 118.7 seconds.
Chain 2 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 2 finished in 123.1 seconds.

All 4 chains finished successfully.
Mean chain execution time: 108.2 seconds.
Total execution time: 123.4 seconds.
# summary(m1)
# conditional_effects(m1)
# pp_check(m1)
# plot(m1)

Compare prior-only model to model with data using priorsense package:


m1_draws <- as_draws_df(m1)

# Extract prior and posterior draws
# powerscale_sensitivity(m1, variable = c("b_Intercept", 
#                                         "b_sin_doy1", "b_cos_doy1",
#                                         "b_sin_doy2", "b_cos_doy2",
#                                         "b_sin_doy3", "b_cos_doy3",
#                                         "sdgp_gpxy", "sigma", "Intercept"))
# 
# powerscale_plot_dens(m1, variable = c("b_Intercept", 
#                                         "b_sin_doy1", "b_cos_doy1",
#                                         "b_sin_doy2", "b_cos_doy2",
#                                         "b_sin_doy3", "b_cos_doy3",
#                                         "sdgp_gpxy", "sigma", "Intercept"))

Predictions by space and time. Here, although we only have data within clusters, we will predict for cells outside of clusters based on covariates. We will also predict for the month for whcih we do not have data.

set up the predictions matrix


#set_up prediction date frame
nd_m1 <- mw_100m_grid_sf %>%
  sf::st_sf() %>%
  mutate(x = st_coordinates(st_centroid(.))[, 1],
         y = st_coordinates(st_centroid(.))[, 2]) %>%
  st_drop_geometry() %>%
  rename(pop_density = 1) %>%
  mutate(pop_density_km2 = pop_density / 0.01)
G2;H2;Warningh: There were 2 warnings in `stopifnot()`.
The first warning was:
ℹ In argument: `x = st_coordinates(st_centroid(.))[, 1]`.
Caused by warning:
! st_centroid assumes attributes are constant over geometries
ℹ Run ]8;;ide:run:dplyr::last_dplyr_]8;;ide:run:warnings()warnings()]8;;dplyr::last_dplyr_]8;;ide:run:warnings()warnings()]8;;]8;; to see the 1 remaining warning.g
#get the first day of each month for prediction
first_days <- ymd(paste0("2020-", sprintf("%02d", 1:12), "-01"))
first_day_doy <- yday(first_days)

#calculate Fourier terms
first_day_seasonality <- tibble(
  mean_doy = first_day_doy,
  sin_doy1 = sin(2 * pi * mean_doy / 365.25),
  cos_doy1 = cos(2 * pi * mean_doy / 365.25),
  sin_doy2 = sin(4 * pi * mean_doy / 365.25),
  cos_doy2 = cos(4 * pi * mean_doy / 365.25),
  sin_doy3 = sin(6 * pi * mean_doy / 365.25),
  cos_doy3 = cos(6 * pi * mean_doy / 365.25),
  date = first_days
)

#expand grid for prediction
nd_m1 <- nd_m1 %>%
  crossing(first_day_seasonality) %>%
  mutate(building_coverage_pct = building_coverage_pct/100)

Get the spatiotemporal predictions for model 1

m1_sum <- get_st_predictions(model = m1, nd=nd_m1, 
                             model_id = "m1", 
                             out_dir = "epred_chunks_m1", 
                             ndraws = 1000, 
                             chunk_size = 10000, 
                             who_pm25_limit = 25)
Processing chunk 1 of 33 
Processing chunk 2 of 33 
Processing chunk 3 of 33 
Processing chunk 4 of 33 
Processing chunk 5 of 33 
Processing chunk 6 of 33 
Processing chunk 7 of 33 
Processing chunk 8 of 33 
Processing chunk 9 of 33 
Processing chunk 10 of 33 
Processing chunk 11 of 33 
Processing chunk 12 of 33 
Processing chunk 13 of 33 
Processing chunk 14 of 33 
Processing chunk 15 of 33 
Processing chunk 16 of 33 
Processing chunk 17 of 33 
Processing chunk 18 of 33 
Processing chunk 19 of 33 
Processing chunk 20 of 33 
Processing chunk 21 of 33 
Processing chunk 22 of 33 
Processing chunk 23 of 33 
Processing chunk 24 of 33 
Processing chunk 25 of 33 
Processing chunk 26 of 33 
Processing chunk 27 of 33 
Processing chunk 28 of 33 
Processing chunk 29 of 33 
Processing chunk 30 of 33 
Processing chunk 31 of 33 
Processing chunk 32 of 33 
Processing chunk 33 of 33 

Plot predictions for model 1

m1_plots <- plot_st_predictions(summary_df = m1_sum, cluster_sf = scale_72_clusters)

m1_plots %>% map(plot)
$mean_log_plot

$mean_plot

$sd_plot

$exceed_plot

Now just draw 50 random coordinates, and predict by time to see whether we captured the trend.

#sample coordinate points from the household dataset
#we will fix the small number sampled later!
set.seed(123) 
sampled_points_m1 <- aq_model_data %>%
  sample_n(50) %>%
  select(x, y)

#generate DOY 0–365 and calculate Fourier terms
doy_grid <- tibble(doy = 0:365) %>%
  mutate(
    sin_doy1 = sin(2 * pi * doy / 365),
    cos_doy1 = cos(2 * pi * doy / 365),
    sin_doy2 = sin(4 * pi * doy / 365),
    cos_doy2 = cos(4 * pi * doy / 365),
    sin_doy3 = sin(6 * pi * doy / 365),
    cos_doy3 = cos(6 * pi * doy / 365)
  )

#expand grid across sampled locations
prediction_df_m1 <- sampled_points_m1 %>%
  crossing(doy_grid)

#add predictions
preds_m1 <- add_epred_draws(object = m1, newdata = prediction_df_m1)

#summarise
preds_m1_sum <- preds_m1 %>%
  ungroup() %>%
  group_by(doy) %>%
  summarise(.epred_mean = mean(.epred),
            .lower = quantile(.epred, probs=0.025),
            .upper = quantile(.epred, probs = 0.975))

#GEt the observed data
observed_data <- aq_model_data %>%
  select(mean_doy, log_pm2_5) 

#plot
preds_m1_sum %>%
  ggplot() +
  geom_ribbon(aes(x=doy, ymin=.lower, ymax = .upper), fill="steelblue", alpha=0.3) +
  geom_line(aes(x=doy, y=.epred_mean)) +
  geom_jitter(data = observed_data, aes(x = mean_doy, y = log_pm2_5),
              color = "darkred", alpha = 0.6, width = 0.5, height = 0.0, size = 1.2) +
  labs(title = "Model-estimated log(PM2.5) with empirical measurements",
       subtitle = "Model predictions with 95% CrI and observed data points",
       x = "Day of year",
       y = "log(PM2.5)",
       caption = "Modelled estimates restricted to within clusters") +
  theme_ggdist() +
  theme(panel.background = element_rect(colour = "grey78"),
        legend.position = "none")

Model 2: With covariates

Now we include grid level covariates of distance to the road, population density, and building footprint percent, along witht the mean temp and huidity on the day of measurement (from the purple air monitors)

Again, priors to be fixed later

priors <- c(
  prior(normal(3.4, 1), class = "Intercept"),
  prior(normal(0,1), class = "b"),
  prior(exponential(1), class = "sigma"),
  prior(exponential(1), class = "sdgp"),
  prior(normal(0, 1), class = "lscale", lb = 0)
)


m2 <- brm(
    formula = log_pm2_5 ~ gp(x, y, k=15)  +
      s(mean_temp_c, k=5) + 
      s(mean_current_humidity, k=5) +
      s(pop_density_km2, k=5) +
      s(building_coverage_pct, k=5) +
      s(dist_to_road_m, k=5) +
      sin_doy1 + cos_doy1 +
      sin_doy2 + cos_doy2 +
      sin_doy3 + cos_doy3,
    data = aq_model_data,
    family = gaussian(),
    prior = priors,
    chains = 4, cores = 4,
    backend = "cmdstanr"
  )
G2;H2;Warningh: The global prior 'normal(0, 1)' of class 'lscale' will not be used in the model as all related coefficients have individual priors already. If you did not set those priors yourself, then maybe brms has assigned default priors. See ?set_prior and ?default_prior for more details.g
G3;Compiling Stan program...
g

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

 
G3;Start sampling
g
Running MCMC with 4 parallel chains...

Chain 1 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 2 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 3 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 4 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 2 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 3 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 1 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 4 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 3 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 2 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 4 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 1 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 2 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 3 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 4 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 1 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 2 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 3 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 4 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 1 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 2 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 3 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 4 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 1 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 2 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 3 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 4 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 1 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 2 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 3 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 4 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 1 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 2 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 3 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 4 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 1 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 2 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 4 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 3 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 1 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 2 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 2 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 4 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 4 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 3 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 3 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 2 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 1 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 1 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 2 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 4 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 3 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 1 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 2 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 2 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 4 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 3 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 1 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 2 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 2 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 4 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 1 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 3 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 2 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 4 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 2 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 1 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 3 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 2 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 4 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 2 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 2 finished in 156.7 seconds.
Chain 1 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 3 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 4 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 1 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 3 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 4 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 1 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 3 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 4 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 1 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 3 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 4 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 1 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 3 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 1 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 4 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 1 finished in 210.8 seconds.
Chain 4 finished in 210.8 seconds.
Chain 3 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 3 finished in 218.8 seconds.

All 4 chains finished successfully.
Mean chain execution time: 199.3 seconds.
Total execution time: 219.1 seconds.
G3;Warning: 8 of 4000 (0.0%) transitions ended with a divergence.
See https://mc-stan.org/misc/warnings for details.

g
summary(m2)
G2;H2;Warningh: There were 8 divergent transitions after warmup. Increasing adapt_delta above 0.8 may help. See http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmupg
 Family: gaussian 
  Links: mu = identity; sigma = identity 
Formula: log_pm2_5 ~ gp(x, y, k = 15) + s(mean_temp_c, k = 5) + s(mean_current_humidity, k = 5) + s(pop_density_km2, k = 5) + s(building_coverage_pct, k = 5) + s(dist_to_road_m, k = 5) + sin_doy1 + cos_doy1 + sin_doy2 + cos_doy2 + sin_doy3 + cos_doy3 
   Data: aq_model_data (Number of observations: 3363) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Smoothing Spline Hyperparameters:
                              Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sds(smean_temp_c_1)               1.74      0.88     0.72     3.96 1.00     2085     2320
sds(smean_current_humidity_1)     1.31      0.85     0.27     3.48 1.00     1563     1074
sds(spop_density_km2_1)           0.73      0.69     0.03     2.54 1.00     1892     2287
sds(sbuilding_coverage_pct_1)     0.69      0.62     0.03     2.28 1.00     1167     1800
sds(sdist_to_road_m_1)            0.33      0.37     0.01     1.33 1.00     2111     2766

Gaussian Process Hyperparameters:
             Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sdgp(gpxy)       2.33      0.86     1.13     4.35 1.00     2698     2676
lscale(gpxy)     0.03      0.01     0.01     0.05 1.00     4910     3193

Regression Coefficients:
                         Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept                    2.87      0.17     2.52     3.20 1.00     4302     2655
sin_doy1                    -0.76      0.07    -0.89    -0.63 1.00     3242     3386
cos_doy1                    -0.49      0.07    -0.63    -0.36 1.00     6380     3122
sin_doy2                     0.09      0.05    -0.01     0.19 1.00     4416     3466
cos_doy2                    -0.05      0.06    -0.16     0.07 1.00     3420     3100
sin_doy3                    -0.17      0.04    -0.24    -0.10 1.00     4903     3054
cos_doy3                     0.12      0.04     0.03     0.20 1.00     5240     2913
smean_temp_c_1               2.35      0.51     1.34     3.33 1.00     6263     2943
smean_current_humidity_1     1.21      0.38     0.45     1.95 1.00     5830     3264
spop_density_km2_1           0.23      0.56    -0.71     1.43 1.00     4434     2733
sbuilding_coverage_pct_1     0.41      0.32    -0.15     1.07 1.00     3499     3346
sdist_to_road_m_1           -0.22      0.35    -0.84     0.57 1.00     4041     3039

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma     0.54      0.01     0.53     0.56 1.00     6747     2877

Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
conditional_effects(m2)

pp_check(m2)
G3;Using 10 posterior draws for ppc type 'dens_overlay' by default.
g

plot(m2)

NA
NA

# m2_draws <- as_draws_df(m2)
# 
# # Extract prior and posterior draws
# powerscale_sensitivity(m2, variable = c("b_Intercept", "b_sin_doy1", "b_cos_doy1", "b_sin_doy2", "b_cos_doy2", "b_sin_doy3", "b_cos_doy3", "bs_smean_temp_c_1", "bs_smean_current_humidity_1", "bs_spop_density_km2_1", "bs_sbuilding_coverage_pct_1", "bs_sdist_to_road_m_1",         
# "sds_smean_temp_c_1", "sds_smean_current_humidity_1", "sds_spop_density_km2_1", "sds_sbuilding_coverage_pct_1", 
# "sds_sdist_to_road_m_1", "sdgp_gpxy", "lscale_gpxy", "sigma", "Intercept", "s_smean_temp_c_1[1]", "s_smean_temp_c_1[2]", "s_smean_temp_c_1[3]",         "s_smean_current_humidity_1[1]",  "s_smean_current_humidity_1[2]",  "s_smean_current_humidity_1[3]",  "s_spop_density_km2_1[1]",       
#  "s_spop_density_km2_1[2]",        "s_spop_density_km2_1[3]",        "s_sbuilding_coverage_pct_1[1]",  "s_sbuilding_coverage_pct_1[2]", 
#  "s_sbuilding_coverage_pct_1[3]",  "s_sdist_to_road_m_1[1]", "s_sdist_to_road_m_1[2]", "s_sdist_to_road_m_1[3]"))
# 
# powerscale_plot_dens(m2, variable = c("b_Intercept", "b_sin_doy1", "b_cos_doy1", "b_sin_doy2", "b_cos_doy2", "b_sin_doy3", "b_cos_doy3", "bs_smean_temp_c_1", "bs_smean_current_humidity_1", "bs_spop_density_km2_1", "bs_sbuilding_coverage_pct_1", "bs_sdist_to_road_m_1",         
# "sds_smean_temp_c_1", "sds_smean_current_humidity_1", "sds_spop_density_km2_1", "sds_sbuilding_coverage_pct_1", 
# "sds_sdist_to_road_m_1", "sdgp_gpxy", "lscale_gpxy", "sigma", "Intercept", "s_smean_temp_c_1[1]", "s_smean_temp_c_1[2]", "s_smean_temp_c_1[3]",         "s_smean_current_humidity_1[1]",  "s_smean_current_humidity_1[2]",  "s_smean_current_humidity_1[3]",  "s_spop_density_km2_1[1]",       
#  "s_spop_density_km2_1[2]",        "s_spop_density_km2_1[3]",        "s_sbuilding_coverage_pct_1[1]",  "s_sbuilding_coverage_pct_1[2]", 
#  "s_sbuilding_coverage_pct_1[3]",  "s_sdist_to_road_m_1[1]", "s_sdist_to_road_m_1[2]", "s_sdist_to_road_m_1[3]"))

Prediction matrix for m2


nd_m2 <- nd_m1 %>%
  

Get predictions for model 2

m2_sum <- get_st_predictions(model = m2, nd=nd_m2, model_id = "m2", out_dir = "epred_chunks_m2", ndraws = 1000, chunk_size = 10000, who_pm25_limit = 25)
G1;H1;Errorh: object 'nd_m2' not found
Error during wrapup: not that many frames on the stack
Error: no more error handlers available (recursive errors?); invoking 'abort' restart
g

Plot predictions for model 2

m2_plots %>% map(plot)
$mean_log_plot

$mean_plot

$sd_plot

$exceed_plot

Sample coordinates, and plot over time


#sample coordinate points
#will sort out the small number later
set.seed(123)
sampled_points_m2 <- aq_model_data %>%
  sample_n(50) %>%
  select(x, y, mean_temp_c, mean_current_humidity, pop_density_km2, building_coverage_pct, dist_to_road_m, grid_id, log_pm2_5)

#generate DOY 0–365 and calculate Fourier terms
doy_grid <- tibble(doy = seq(0,365, by=1)) %>%
  mutate(
    sin_doy1 = sin(2 * pi * doy / 365),
    cos_doy1 = cos(2 * pi * doy / 365),
    sin_doy2 = sin(4 * pi * doy / 365),
    cos_doy2 = cos(4 * pi * doy / 365),
    sin_doy3 = sin(6 * pi * doy / 365),
    cos_doy3 = cos(6 * pi * doy / 365)
  )

#expand grid across sampled locations
prediction_df_m2 <- sampled_points_m2 %>%
  crossing(doy_grid)

#add predictions
preds_m2 <- add_epred_draws(object = m2, newdata = prediction_df_m2, ndraws = 100)

#observed data for plotting
observed_data <- aq_model_data %>%
  select(mean_doy, log_pm2_5) %>%
  mutate(mean_doy = round(mean_doy))

#plot
preds_m2 %>%
  ungroup() %>%
  ggplot(aes(x=doy)) +
  stat_lineribbon(aes(y=.epred), .width=0.95) + 
  geom_jitter(data = observed_data, aes(x = mean_doy, y = log_pm2_5),
              color = "darkred", alpha = 0.6, width = 0.5, height = 0.0, size = 1.2) +
  scale_fill_brewer() +
  labs(title = "Model-estimated log(PM2.5) with empirical measurements",
       subtitle = "Model predictions with 95% CrI and observed data points",
       x = "Day of year",
       y = "log(PM2.5)",
       caption = "Modelled estimates restricted to within clusters") +
  theme_ggdist() +
  theme(panel.background = element_rect(colour = "grey78"),
        legend.position = "none")

Compare models, using LOO CV


library(loo)

loo_m1 <- loo(m1)
loo_m2 <- loo(m2)

loo_compare(loo_m1, loo_m2)

PM10

Now to model PM10 data

TODO

LS0tCnRpdGxlOiAiQmxhbnR5cmUgQWlyIFF1YWxpdHkgTW9kZWxsaW5nIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBMaWJyYXJpZXMKCkxvYWQgcmVxdWlyZWQgbGlicmFyaWVzCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoaGVyZSkKbGlicmFyeShicm1zKQpsaWJyYXJ5KHRpZHliYXllcykKbGlicmFyeShzZikKbGlicmFyeShtZ2N2KQpsaWJyYXJ5KHByaW9yc2Vuc2UpCmxpYnJhcnkob3NtZGF0YSkKbGlicmFyeShubmdlbykKbGlicmFyeShnbHVlKQpsaWJyYXJ5KGFycm93KQpgYGAKCiMjIEltcG9ydCBkYXRhCgpJbXBvcnQgdGhlIG1vZGVsbGluZyBkYXRhc2V0IC0gY2xlYW5lZCBhbmQgcHJlcHBlZCBpbiB0aGUgYDIwMjUtMDUtMTJfYXFfY2xlYW4uUm1kYCBzY3JpcHQKCmBgYHtyfQoKYXFfbW9kZWxfZGF0YSA8LSByZWFkX3JkcygiaW5wdXRfZGF0YS9hcV9tb2RlbF9kYXRhLnJkcyIpCgojYWxzbyBsb2FkIHRoZSBzY2FsZSBjbHVzdGVycwpsb2FkKCJpbnB1dF9kYXRhL3NjYWxlXzcyX2NsdXN0ZXJzLnJkYSIpICNTQ0FMRSBjbHVzdGVycwoKI2FuZCB0aGUgZ3JpZCBkYXRhCm13XzEwMG1fY3JvcHBlZCA8LSByZWFkX3JkcygiaW5wdXRfZGF0YS9td18xMDBtX2Nyb3BwZWQucmRzIikKbXdfMTAwbV9ncmlkX3NmIDwtIHJlYWRfcmRzKCJpbnB1dF9kYXRhL213XzEwMG1fZ3JpZF9zZi5yZHMiKQoKCmBgYAoKCiMjIEZ1bmN0aW9ucwoKYGdldF9zdF9wcmVkaWN0aW9ucygpYDogQSBmdW5jdGlvbiB0byB0YWtlIHBvc3RlcmlvciBkcmF3cyBmb3IgYSBwcmVkaWN0aW9uIG1hdHJpeCwgd3JpdGUgdG8gYW4gYGFycm93YCBkYXRhYmFzZSAodG8gaGFuZGxlIGV4aGF1c3RpbmcgbWVtb3J5KSwgdGhlbiBzdW1tYXJpc2UKCmBgYHtyfQoKZ2V0X3N0X3ByZWRpY3Rpb25zIDwtIGZ1bmN0aW9uKG1vZGVsLCBuZCwgbW9kZWxfaWQsIG91dF9kaXIsIG5kcmF3cyA9IDEwMDAsIGNodW5rX3NpemUgPSAxMDAwMCwgd2hvX3BtMjVfbGltaXQgPSAyNSkgewogICMgQ3JlYXRlIG91dHB1dCBmb2xkZXIKICBkaXIuY3JlYXRlKG91dF9kaXIsIHNob3dXYXJuaW5ncyA9IEZBTFNFLCByZWN1cnNpdmUgPSBUUlVFKQoKICBuZCA8LSBuZCAlPiUgbXV0YXRlKC5yb3cgPSByb3dfbnVtYmVyKCkpCiAgbl9jaHVua3MgPC0gY2VpbGluZyhucm93KG5kKSAvIGNodW5rX3NpemUpCgogICMgU2F2ZSBkZXNpZ24gbWF0cml4IHRvIGRpc2sgZm9yIGxhdGVyIGpvaW5zCiAgbmRfZmlsZSA8LSBmaWxlLnBhdGgob3V0X2RpciwgcGFzdGUwKCJuZF8iLCBtb2RlbF9pZCwgIi5wYXJxdWV0IikpCiAgYXJyb3c6OndyaXRlX3BhcnF1ZXQobmQsIG5kX2ZpbGUpCgogICMgTG9vcCBvdmVyIGNodW5rcwogIGZvciAoaSBpbiBzZXFfbGVuKG5fY2h1bmtzKSkgewogICAgY2F0KCJQcm9jZXNzaW5nIGNodW5rIiwgaSwgIm9mIiwgbl9jaHVua3MsICJcbiIpCgogICAgaWR4IDwtICgoaSAtIDEpICogY2h1bmtfc2l6ZSArIDEpOm1pbihpICogY2h1bmtfc2l6ZSwgbnJvdyhuZCkpCiAgICBuZF9jaHVuayA8LSBuZFtpZHgsIF0KCiAgICBlcHJlZF9hcnJheSA8LSBwb3N0ZXJpb3JfZXByZWQobW9kZWwsIG5ld2RhdGEgPSBuZF9jaHVuaywgbmRyYXdzID0gbmRyYXdzKQoKICAgIGVwcmVkX2RmIDwtIGFzLmRhdGEuZnJhbWUudGFibGUoZXByZWRfYXJyYXksIHJlc3BvbnNlTmFtZSA9ICJlcHJlZCIpCiAgICBjb2xuYW1lcyhlcHJlZF9kZikgPC0gYygiZHJhdyIsICJyb3dfaW5fY2h1bmsiLCAiZXByZWQiKQoKICAgIGVwcmVkX2RmIDwtIGVwcmVkX2RmICU+JQogICAgICBtdXRhdGUoCiAgICAgICAgZHJhdyA9IGFzLmludGVnZXIoZHJhdyksCiAgICAgICAgcm93X2luX2NodW5rID0gYXMuaW50ZWdlcihyb3dfaW5fY2h1bmspLAogICAgICAgIC5yb3cgPSBpZHhbcm93X2luX2NodW5rXSwKICAgICAgICBtb2RlbF9pZCA9IG1vZGVsX2lkCiAgICAgICkgJT4lCiAgICAgIHNlbGVjdChkcmF3LCAucm93LCBlcHJlZCwgbW9kZWxfaWQpCgogICAgYXJyb3c6OndyaXRlX3BhcnF1ZXQoCiAgICAgIGVwcmVkX2RmLAogICAgICBzaW5rID0gZmlsZS5wYXRoKG91dF9kaXIsIGdsdWU6OmdsdWUoImVwcmVkX2NodW5rX3tpfS5wYXJxdWV0IikpCiAgICApCgogICAgcm0oZXByZWRfYXJyYXksIGVwcmVkX2RmLCBuZF9jaHVuaykKICAgIGdjKCkKICB9CgogICMgUmVhZCBmcm9tIGRpc2sKICBkcyA8LSBhcnJvdzo6b3Blbl9kYXRhc2V0KG91dF9kaXIpCiAgbmRfZHMgPC0gYXJyb3c6Om9wZW5fZGF0YXNldChuZF9maWxlKQoKICBzdW1tYXJ5X2RmIDwtIGRzICU+JQogICAgbXV0YXRlKGVwcmVkX3BtMjUgPSBleHAoZXByZWQpKSAlPiUKICAgIGdyb3VwX2J5KC5yb3cpICU+JQogICAgc3VtbWFyaXNlKAogICAgICBtZWFuX2xvZ19lcHJlZCA9IG1lYW4oZXByZWQsIG5hLnJtID0gVFJVRSksCiAgICAgIHNkX2xvZ19lcHJlZCA9IHNkKGVwcmVkLCBuYS5ybSA9IFRSVUUpLAogICAgICBsd3JfbG9nID0gcXVhbnRpbGUoZXByZWQsIDAuMDI1LCBuYS5ybSA9IFRSVUUpLAogICAgICB1cHJfbG9nID0gcXVhbnRpbGUoZXByZWQsIDAuOTc1LCBuYS5ybSA9IFRSVUUpLAogICAgICAKICAgICAgbWVhbl9lcHJlZCA9IG1lYW4oZXByZWRfcG0yNSwgbmEucm0gPSBUUlVFKSwKICAgICAgc2RfZXByZWQgPSBzZChlcHJlZF9wbTI1LCBuYS5ybSA9IFRSVUUpLAogICAgICBsd3IgPSBxdWFudGlsZShlcHJlZF9wbTI1LCAwLjAyNSwgbmEucm0gPSBUUlVFKSwKICAgICAgdXByID0gcXVhbnRpbGUoZXByZWRfcG0yNSwgMC45NzUsIG5hLnJtID0gVFJVRSksCiAgICAgIAogICAgICBwcm9iX2V4Y2VlZCA9IG1lYW4oZXByZWRfcG0yNSA+IHdob19wbTI1X2xpbWl0LCBuYS5ybSA9IFRSVUUpLAogICAgICAuZ3JvdXBzID0gImRyb3AiCiAgICApICU+JQogICAgY29sbGVjdCgpICU+JQogICAgbGVmdF9qb2luKG5kLCBieSA9ICIucm93IikgJT4lCiAgICBtdXRhdGUoZGF0ZSA9IGx1YnJpZGF0ZTo6bW9udGgoZGF0ZSwgbGFiZWwgPSBUUlVFKSkKCiAgcmV0dXJuKHN1bW1hcnlfZGYpCn0KCgpgYGAKCmBwbG90X3N0X3ByZWRpY3Rpb25zKClgOiBBIGZ1bmN0aW9uIHRvIHBsb3Qgc3BhdGlvdGVtcG9yYWwgcHJlZGljdGlvbnMgZnJvbSBtb2RlbCBwb3N0ZXJpb3JzCgpgYGB7cn0KCnBsb3Rfc3RfcHJlZGljdGlvbnMgPC0gZnVuY3Rpb24oc3VtbWFyeV9kZiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9zZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZV9saW1pdHMgPSBsaXN0KG1lYW4gPSBOVUxMLCBzZCA9IE5VTEwsIHByb2IgPSBjKDAsIDEpKSkgewogIHJlcXVpcmUoZ2dwbG90MikKICByZXF1aXJlKHZpcmlkaXMpCiAgcmVxdWlyZShnZ2Rpc3QpCiAgcmVxdWlyZShkcGx5cikKICByZXF1aXJlKGx1YnJpZGF0ZSkKCiAgIyBNZWFuIHByZWRpY3Rpb24gcGxvdHNzCiAgICBwMSA8LSBzdW1tYXJ5X2RmICU+JQogICAgZ2dwbG90KCkgKwogICAgZ2VvbV90aWxlKGFlcyh4ID0geCwgeSA9IHksIGZpbGwgPSBtZWFuX2xvZ19lcHJlZCkpICsKICAgIGdlb21fc2YoZGF0YSA9IGNsdXN0ZXJfc2YsIGNvbG91ciA9ICJncmV5NzgiLCBmaWxsID0gTkEpICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJEIiwgbGltaXRzID0gdmFsdWVfbGltaXRzJG1lYW4pICsKICAgIGZhY2V0X3dyYXAofmRhdGUpICsKICAgIGxhYnModGl0bGUgPSAiUG9zdGVyaW9yIG1lYW4gb2YgbG9nKFBNMi41KSAowrVnL23CsykiLCB4ID0gIiIsIHkgPSAiIikgKwogICAgdGhlbWVfZ2dkaXN0KCkgKwogICAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG91ciA9ICJncmV5NzgiKSwKICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiKSkKICAgIAogIHAyIDwtIHN1bW1hcnlfZGYgJT4lCiAgICBnZ3Bsb3QoKSArCiAgICBnZW9tX3RpbGUoYWVzKHggPSB4LCB5ID0geSwgZmlsbCA9IG1lYW5fZXByZWQpKSArCiAgICBnZW9tX3NmKGRhdGEgPSBjbHVzdGVyX3NmLCBjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbCA9IE5BKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiRCIsIGxpbWl0cyA9IHZhbHVlX2xpbWl0cyRtZWFuKSArCiAgICBmYWNldF93cmFwKH5kYXRlKSArCiAgICBsYWJzKHRpdGxlID0gIlBvc3RlcmlvciBtZWFuIG9mIFBNMi41ICjCtWcvbcKzKSIsIHggPSAiIiwgeSA9ICIiKSArCiAgICB0aGVtZV9nZ2Rpc3QoKSArCiAgICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3VyID0gImdyZXk3OCIpLAogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIpKQoKICAjIFN0YW5kYXJkIGRldmlhdGlvbiBwbG90CiAgcDMgPC0gc3VtbWFyeV9kZiAlPiUKICAgIGdncGxvdCgpICsKICAgIGdlb21fdGlsZShhZXMoeCA9IHgsIHkgPSB5LCBmaWxsID0gc2RfZXByZWQpKSArCiAgICBnZW9tX3NmKGRhdGEgPSBjbHVzdGVyX3NmLCBjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbCA9IE5BKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiRiIsIGxpbWl0cyA9IHZhbHVlX2xpbWl0cyRzZCkgKwogICAgZmFjZXRfd3JhcCh+ZGF0ZSkgKwogICAgbGFicyh0aXRsZSA9ICJQb3N0ZXJpb3IgU0Qgb2YgUE0yLjUgKMK1Zy9twrMpIiwgeCA9ICIiLCB5ID0gIiIpICsKICAgIHRoZW1lX2dnZGlzdCgpICsKICAgIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvdXIgPSAiZ3JleTc4IiksCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IikpCgogICMgRXhjZWVkYW5jZSBwcm9iYWJpbGl0eSBwbG90CiAgcDQgPC0gc3VtbWFyeV9kZiAlPiUKICAgIGdncGxvdCgpICsKICAgIGdlb21fdGlsZShhZXMoeCA9IHgsIHkgPSB5LCBmaWxsID0gcHJvYl9leGNlZWQpKSArCiAgICBnZW9tX3NmKGRhdGEgPSBjbHVzdGVyX3NmLCBjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbCA9IE5BKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiQyIsIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSwgbGltaXRzID0gdmFsdWVfbGltaXRzJHByb2IpICsKICAgIGZhY2V0X3dyYXAofmRhdGUpICsKICAgIGxhYnModGl0bGUgPSAiUHIoUE0yLjUgPiAyNSDCtWcvbcKzKSIsIHggPSAiIiwgeSA9ICIiKSArCiAgICB0aGVtZV9nZ2Rpc3QoKSArCiAgICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3VyID0gImdyZXk3OCIpLAogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIpKQoKICBsaXN0KG1lYW5fbG9nX3Bsb3QgPSBwMSwgbWVhbl9wbG90ID0gcDIsIHNkX3Bsb3QgPSBwMywgZXhjZWVkX3Bsb3QgPSBwNCkKfQoKCgoKYGBgCgoKYGNvbXB1dGVfZXhwb3N1cmVfbWV0cmljcygpYCBjYWxjdWxhdGVzIGV4cG9zdXJlIG1ldHJpY3MgZm9yIHRoZSAxIHllYXIgcAoKCiMjIFBNMi41CgpGaXJzdCB3ZSB3aWxsIG1vZGVsIFBNMi41CgojIyMgTW9kZWwgMTogRGF5IG9mIHllYXIgb25seQoKCiJXZSBmaXR0ZWQgYSBzcGF0aWFsbHkgc21vb3RoIHJlZ3Jlc3Npb24gbW9kZWwgZm9yIGxvZy10cmFuc2Zvcm1lZCBQTTIuNSBjb25jZW50cmF0aW9ucyB1c2luZyBhIEdhdXNzaWFuIHByb2Nlc3MgKEdQKSBzbW9vdGggb3ZlciBzcGF0aWFsIGNvb3JkaW5hdGVzICh4LCB5KSB0byBjYXB0dXJlIGZsZXhpYmxlIHNwYXRpYWwgdHJlbmRzLiBTZWFzb25hbCB2YXJpYXRpb24gd2FzIG1vZGVsbGVkIHVzaW5nIGEgRm91cmllciBzZXJpZXMgZXhwYW5zaW9uIG9mIGRheS1vZi15ZWFyIHVwIHRvIHRoZSAzcmQgaGFybW9uaWMgKGkuZS4sIHNpbmUgYW5kIGNvc2luZSB0ZXJtcyBmb3IgYW5udWFsLCBzZW1pLWFubnVhbCwgYW5kIHRyaS1hbm51YWwgY3ljbGVzKSB0byBhY2NvdW50IGZvciBjb21wbGV4IHNlYXNvbmFsIHBhdHRlcm5zLiBUaGUgbW9kZWwgd2FzIGZpdHRlZCBpbiBhIEJheWVzaWFuIGZyYW1ld29yayB1c2luZyBicm1zIHdpdGggYSBHYXVzc2lhbiBsaWtlbGlob29kIGFuZCB0aGUgY21kc3RhbnIgYmFja2VuZC4iCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQoKcHJpb3JzIDwtIGMoCiAgcHJpb3Iobm9ybWFsKDMuNCwgMSksIGNsYXNzID0gIkludGVyY2VwdCIpLAogIHByaW9yKG5vcm1hbCgwLDEpLCBjbGFzcyA9ICJiIiksCiAgcHJpb3IoZXhwb25lbnRpYWwoMSksIGNsYXNzID0gInNpZ21hIiksCiAgcHJpb3IoZXhwb25lbnRpYWwoMSksIGNsYXNzID0gInNkZ3AiKSwKICBwcmlvcihub3JtYWwoMCwgMSksIGNsYXNzID0gImxzY2FsZSIsIGxiID0gMCkKKQoKYGBgCgpGaXQgcHJpb3Igb25seSBtb2RlbAoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMn0KCm0xX3ByaW9yIDwtIGJybSgKICAgIGZvcm11bGEgPSBsb2dfcG0yXzUgfiBncCh4LCB5LCBrPTE1KSArCiAgICAgIHNpbl9kb3kxICsgY29zX2RveTEgKwogICAgICBzaW5fZG95MiArIGNvc19kb3kyICsKICAgICAgc2luX2RveTMgKyBjb3NfZG95MywKICAgIGRhdGEgPSBhcV9tb2RlbF9kYXRhLAogICAgZmFtaWx5ID0gZ2F1c3NpYW4oKSwKICAgIHByaW9yID0gcHJpb3JzLAogICAgc2FtcGxlX3ByaW9yID0gIm9ubHkiLAogICAgY2hhaW5zID0gNCwgY29yZXMgPSA0LAogICAgYmFja2VuZCA9ICJjbWRzdGFuciIKICApCgoKIyBzdW1tYXJ5KG0xX3ByaW9yKQojIHBsb3QobTFfcHJpb3IpCgpgYGAKCk5vdyBtb2RlbCB3aXRoIGRhdGEuCgoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMn0KbTEgPC0gYnJtKAogICAgZm9ybXVsYSA9IGxvZ19wbTJfNSB+IGdwKHgsIHksIGs9MTUpICArCiAgICBzaW5fZG95MSArIGNvc19kb3kxICsKICAgIHNpbl9kb3kyICsgY29zX2RveTIgKwogICAgc2luX2RveTMgKyBjb3NfZG95MywKICAgIGRhdGEgPSBhcV9tb2RlbF9kYXRhLAogICAgZmFtaWx5ID0gZ2F1c3NpYW4oKSwKICAgIHByaW9yID0gcHJpb3JzLAogICAgY2hhaW5zID0gNCwgY29yZXMgPSA0LAogICAgYmFja2VuZCA9ICJjbWRzdGFuciIKICApCgojIHN1bW1hcnkobTEpCiMgY29uZGl0aW9uYWxfZWZmZWN0cyhtMSkKIyBwcF9jaGVjayhtMSkKIyBwbG90KG0xKQoKYGBgCgpDb21wYXJlIHByaW9yLW9ubHkgbW9kZWwgdG8gbW9kZWwgd2l0aCBkYXRhIHVzaW5nIGBwcmlvcnNlbnNlYCBwYWNrYWdlOgoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMn0KCm0xX2RyYXdzIDwtIGFzX2RyYXdzX2RmKG0xKQoKIyBFeHRyYWN0IHByaW9yIGFuZCBwb3N0ZXJpb3IgZHJhd3MKIyBwb3dlcnNjYWxlX3NlbnNpdGl2aXR5KG0xLCB2YXJpYWJsZSA9IGMoImJfSW50ZXJjZXB0IiwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJiX3Npbl9kb3kxIiwgImJfY29zX2RveTEiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYl9zaW5fZG95MiIsICJiX2Nvc19kb3kyIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImJfc2luX2RveTMiLCAiYl9jb3NfZG95MyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzZGdwX2dweHkiLCAic2lnbWEiLCAiSW50ZXJjZXB0IikpCiMgCiMgcG93ZXJzY2FsZV9wbG90X2RlbnMobTEsIHZhcmlhYmxlID0gYygiYl9JbnRlcmNlcHQiLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImJfc2luX2RveTEiLCAiYl9jb3NfZG95MSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJiX3Npbl9kb3kyIiwgImJfY29zX2RveTIiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYl9zaW5fZG95MyIsICJiX2Nvc19kb3kzIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNkZ3BfZ3B4eSIsICJzaWdtYSIsICJJbnRlcmNlcHQiKSkKYGBgCgpQcmVkaWN0aW9ucyBieSBzcGFjZSBhbmQgdGltZS4gSGVyZSwgYWx0aG91Z2ggd2Ugb25seSBoYXZlIGRhdGEgd2l0aGluIGNsdXN0ZXJzLCB3ZSB3aWxsIHByZWRpY3QgZm9yIGNlbGxzIG91dHNpZGUgb2YgY2x1c3RlcnMgYmFzZWQgb24gY292YXJpYXRlcy4KV2Ugd2lsbCBhbHNvIHByZWRpY3QgZm9yIHRoZSBtb250aCBmb3Igd2hjaWggd2UgZG8gbm90IGhhdmUgZGF0YS4KCnNldCB1cCB0aGUgcHJlZGljdGlvbnMgbWF0cml4CgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQoKI3NldF91cCBwcmVkaWN0aW9uIGRhdGUgZnJhbWUKbmRfbTEgPC0gbXdfMTAwbV9ncmlkX3NmICU+JQogIHNmOjpzdF9zZigpICU+JQogIG11dGF0ZSh4ID0gc3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoLikpWywgMV0sCiAgICAgICAgIHkgPSBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZCguKSlbLCAyXSkgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQogIHJlbmFtZShwb3BfZGVuc2l0eSA9IDEpICU+JQogIG11dGF0ZShwb3BfZGVuc2l0eV9rbTIgPSBwb3BfZGVuc2l0eSAvIDAuMDEpCgojZ2V0IHRoZSBmaXJzdCBkYXkgb2YgZWFjaCBtb250aCBmb3IgcHJlZGljdGlvbgpmaXJzdF9kYXlzIDwtIHltZChwYXN0ZTAoIjIwMjAtIiwgc3ByaW50ZigiJTAyZCIsIDE6MTIpLCAiLTAxIikpCmZpcnN0X2RheV9kb3kgPC0geWRheShmaXJzdF9kYXlzKQoKI2NhbGN1bGF0ZSBGb3VyaWVyIHRlcm1zCmZpcnN0X2RheV9zZWFzb25hbGl0eSA8LSB0aWJibGUoCiAgbWVhbl9kb3kgPSBmaXJzdF9kYXlfZG95LAogIHNpbl9kb3kxID0gc2luKDIgKiBwaSAqIG1lYW5fZG95IC8gMzY1LjI1KSwKICBjb3NfZG95MSA9IGNvcygyICogcGkgKiBtZWFuX2RveSAvIDM2NS4yNSksCiAgc2luX2RveTIgPSBzaW4oNCAqIHBpICogbWVhbl9kb3kgLyAzNjUuMjUpLAogIGNvc19kb3kyID0gY29zKDQgKiBwaSAqIG1lYW5fZG95IC8gMzY1LjI1KSwKICBzaW5fZG95MyA9IHNpbig2ICogcGkgKiBtZWFuX2RveSAvIDM2NS4yNSksCiAgY29zX2RveTMgPSBjb3MoNiAqIHBpICogbWVhbl9kb3kgLyAzNjUuMjUpLAogIGRhdGUgPSBmaXJzdF9kYXlzCikKCiNleHBhbmQgZ3JpZCBmb3IgcHJlZGljdGlvbgpuZF9tMSA8LSBuZF9tMSAlPiUKICBjcm9zc2luZyhmaXJzdF9kYXlfc2Vhc29uYWxpdHkpICU+JQogIG11dGF0ZShidWlsZGluZ19jb3ZlcmFnZV9wY3QgPSBidWlsZGluZ19jb3ZlcmFnZV9wY3QvMTAwKQoKYGBgCgoKR2V0IHRoZSBzcGF0aW90ZW1wb3JhbCBwcmVkaWN0aW9ucyBmb3IgbW9kZWwgMQoKCmBgYHtyfQptMV9zdW0gPC0gZ2V0X3N0X3ByZWRpY3Rpb25zKG1vZGVsID0gbTEsIG5kPW5kX20xLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9pZCA9ICJtMSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dF9kaXIgPSAiZXByZWRfY2h1bmtzX20xIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmRyYXdzID0gMTAwMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2h1bmtfc2l6ZSA9IDEwMDAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aG9fcG0yNV9saW1pdCA9IDI1KQoKYGBgCgpQbG90IHByZWRpY3Rpb25zIGZvciBtb2RlbCAxCgpgYGB7cn0KbTFfcGxvdHMgPC0gcGxvdF9zdF9wcmVkaWN0aW9ucyhzdW1tYXJ5X2RmID0gbTFfc3VtLCBjbHVzdGVyX3NmID0gc2NhbGVfNzJfY2x1c3RlcnMpCgptMV9wbG90cyAlPiUgbWFwKHBsb3QpCgpgYGAKCgoKTm93IGp1c3QgZHJhdyA1MCByYW5kb20gY29vcmRpbmF0ZXMsIGFuZCBwcmVkaWN0IGJ5IHRpbWUgdG8gc2VlIHdoZXRoZXIgd2UgY2FwdHVyZWQgdGhlIHRyZW5kLgoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMn0KI3NhbXBsZSBjb29yZGluYXRlIHBvaW50cyBmcm9tIHRoZSBob3VzZWhvbGQgZGF0YXNldAojd2Ugd2lsbCBmaXggdGhlIHNtYWxsIG51bWJlciBzYW1wbGVkIGxhdGVyIQpzZXQuc2VlZCgxMjMpIApzYW1wbGVkX3BvaW50c19tMSA8LSBhcV9tb2RlbF9kYXRhICU+JQogIHNhbXBsZV9uKDUwKSAlPiUKICBzZWxlY3QoeCwgeSkKCiNnZW5lcmF0ZSBET1kgMOKAkzM2NSBhbmQgY2FsY3VsYXRlIEZvdXJpZXIgdGVybXMKZG95X2dyaWQgPC0gdGliYmxlKGRveSA9IDA6MzY1KSAlPiUKICBtdXRhdGUoCiAgICBzaW5fZG95MSA9IHNpbigyICogcGkgKiBkb3kgLyAzNjUpLAogICAgY29zX2RveTEgPSBjb3MoMiAqIHBpICogZG95IC8gMzY1KSwKICAgIHNpbl9kb3kyID0gc2luKDQgKiBwaSAqIGRveSAvIDM2NSksCiAgICBjb3NfZG95MiA9IGNvcyg0ICogcGkgKiBkb3kgLyAzNjUpLAogICAgc2luX2RveTMgPSBzaW4oNiAqIHBpICogZG95IC8gMzY1KSwKICAgIGNvc19kb3kzID0gY29zKDYgKiBwaSAqIGRveSAvIDM2NSkKICApCgojZXhwYW5kIGdyaWQgYWNyb3NzIHNhbXBsZWQgbG9jYXRpb25zCnByZWRpY3Rpb25fZGZfbTEgPC0gc2FtcGxlZF9wb2ludHNfbTEgJT4lCiAgY3Jvc3NpbmcoZG95X2dyaWQpCgojYWRkIHByZWRpY3Rpb25zCnByZWRzX20xIDwtIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSBtMSwgbmV3ZGF0YSA9IHByZWRpY3Rpb25fZGZfbTEpCgojc3VtbWFyaXNlCnByZWRzX20xX3N1bSA8LSBwcmVkc19tMSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoZG95KSAlPiUKICBzdW1tYXJpc2UoLmVwcmVkX21lYW4gPSBtZWFuKC5lcHJlZCksCiAgICAgICAgICAgIC5sb3dlciA9IHF1YW50aWxlKC5lcHJlZCwgcHJvYnM9MC4wMjUpLAogICAgICAgICAgICAudXBwZXIgPSBxdWFudGlsZSguZXByZWQsIHByb2JzID0gMC45NzUpKQoKI0dFdCB0aGUgb2JzZXJ2ZWQgZGF0YQpvYnNlcnZlZF9kYXRhIDwtIGFxX21vZGVsX2RhdGEgJT4lCiAgc2VsZWN0KG1lYW5fZG95LCBsb2dfcG0yXzUpIAoKI3Bsb3QKcHJlZHNfbTFfc3VtICU+JQogIGdncGxvdCgpICsKICBnZW9tX3JpYmJvbihhZXMoeD1kb3ksIHltaW49Lmxvd2VyLCB5bWF4ID0gLnVwcGVyKSwgZmlsbD0ic3RlZWxibHVlIiwgYWxwaGE9MC4zKSArCiAgZ2VvbV9saW5lKGFlcyh4PWRveSwgeT0uZXByZWRfbWVhbikpICsKICBnZW9tX2ppdHRlcihkYXRhID0gb2JzZXJ2ZWRfZGF0YSwgYWVzKHggPSBtZWFuX2RveSwgeSA9IGxvZ19wbTJfNSksCiAgICAgICAgICAgICAgY29sb3IgPSAiZGFya3JlZCIsIGFscGhhID0gMC42LCB3aWR0aCA9IDAuNSwgaGVpZ2h0ID0gMC4wLCBzaXplID0gMS4yKSArCiAgbGFicyh0aXRsZSA9ICJNb2RlbC1lc3RpbWF0ZWQgbG9nKFBNMi41KSB3aXRoIGVtcGlyaWNhbCBtZWFzdXJlbWVudHMiLAogICAgICAgc3VidGl0bGUgPSAiTW9kZWwgcHJlZGljdGlvbnMgd2l0aCA5NSUgQ3JJIGFuZCBvYnNlcnZlZCBkYXRhIHBvaW50cyIsCiAgICAgICB4ID0gIkRheSBvZiB5ZWFyIiwKICAgICAgIHkgPSAibG9nKFBNMi41KSIsCiAgICAgICBjYXB0aW9uID0gIk1vZGVsbGVkIGVzdGltYXRlcyByZXN0cmljdGVkIHRvIHdpdGhpbiBjbHVzdGVycyIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKYGBgCgojIyMgTW9kZWwgMjogV2l0aCBjb3ZhcmlhdGVzCgpOb3cgd2UgaW5jbHVkZSBncmlkIGxldmVsIGNvdmFyaWF0ZXMgb2YgZGlzdGFuY2UgdG8gdGhlIHJvYWQsIHBvcHVsYXRpb24gZGVuc2l0eSwgYW5kIGJ1aWxkaW5nIGZvb3RwcmludCBwZXJjZW50LCBhbG9uZyB3aXRodCB0aGUgbWVhbiB0ZW1wIGFuZCBodWlkaXR5IG9uIHRoZSBkYXkgb2YgbWVhc3VyZW1lbnQgKGZyb20gdGhlIHB1cnBsZSBhaXIgbW9uaXRvcnMpCgpBZ2FpbiwgcHJpb3JzIHRvIGJlIGZpeGVkIGxhdGVyCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQpwcmlvcnMgPC0gYygKICBwcmlvcihub3JtYWwoMy40LCAxKSwgY2xhc3MgPSAiSW50ZXJjZXB0IiksCiAgcHJpb3Iobm9ybWFsKDAsMSksIGNsYXNzID0gImIiKSwKICBwcmlvcihleHBvbmVudGlhbCgxKSwgY2xhc3MgPSAic2lnbWEiKSwKICBwcmlvcihleHBvbmVudGlhbCgxKSwgY2xhc3MgPSAic2RncCIpLAogIHByaW9yKG5vcm1hbCgwLCAxKSwgY2xhc3MgPSAibHNjYWxlIiwgbGIgPSAwKQopCgoKbTIgPC0gYnJtKAogICAgZm9ybXVsYSA9IGxvZ19wbTJfNSB+IGdwKHgsIHksIGs9MTUpICArCiAgICAgIHMobWVhbl90ZW1wX2MsIGs9NSkgKyAKICAgICAgcyhtZWFuX2N1cnJlbnRfaHVtaWRpdHksIGs9NSkgKwogICAgICBzKHBvcF9kZW5zaXR5X2ttMiwgaz01KSArCiAgICAgIHMoYnVpbGRpbmdfY292ZXJhZ2VfcGN0LCBrPTUpICsKICAgICAgcyhkaXN0X3RvX3JvYWRfbSwgaz01KSArCiAgICAgIHNpbl9kb3kxICsgY29zX2RveTEgKwogICAgICBzaW5fZG95MiArIGNvc19kb3kyICsKICAgICAgc2luX2RveTMgKyBjb3NfZG95MywKICAgIGRhdGEgPSBhcV9tb2RlbF9kYXRhLAogICAgZmFtaWx5ID0gZ2F1c3NpYW4oKSwKICAgIHByaW9yID0gcHJpb3JzLAogICAgY2hhaW5zID0gNCwgY29yZXMgPSA0LAogICAgYmFja2VuZCA9ICJjbWRzdGFuciIKICApCgpzdW1tYXJ5KG0yKQpjb25kaXRpb25hbF9lZmZlY3RzKG0yKQpwcF9jaGVjayhtMikKcGxvdChtMikKCgpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTJ9CgojIG0yX2RyYXdzIDwtIGFzX2RyYXdzX2RmKG0yKQojIAojICMgRXh0cmFjdCBwcmlvciBhbmQgcG9zdGVyaW9yIGRyYXdzCiMgcG93ZXJzY2FsZV9zZW5zaXRpdml0eShtMiwgdmFyaWFibGUgPSBjKCJiX0ludGVyY2VwdCIsICJiX3Npbl9kb3kxIiwgImJfY29zX2RveTEiLCAiYl9zaW5fZG95MiIsICJiX2Nvc19kb3kyIiwgImJfc2luX2RveTMiLCAiYl9jb3NfZG95MyIsICJic19zbWVhbl90ZW1wX2NfMSIsICJic19zbWVhbl9jdXJyZW50X2h1bWlkaXR5XzEiLCAiYnNfc3BvcF9kZW5zaXR5X2ttMl8xIiwgImJzX3NidWlsZGluZ19jb3ZlcmFnZV9wY3RfMSIsICJic19zZGlzdF90b19yb2FkX21fMSIsICAgICAgICAgCiMgInNkc19zbWVhbl90ZW1wX2NfMSIsICJzZHNfc21lYW5fY3VycmVudF9odW1pZGl0eV8xIiwgInNkc19zcG9wX2RlbnNpdHlfa20yXzEiLCAic2RzX3NidWlsZGluZ19jb3ZlcmFnZV9wY3RfMSIsIAojICJzZHNfc2Rpc3RfdG9fcm9hZF9tXzEiLCAic2RncF9ncHh5IiwgImxzY2FsZV9ncHh5IiwgInNpZ21hIiwgIkludGVyY2VwdCIsICJzX3NtZWFuX3RlbXBfY18xWzFdIiwgInNfc21lYW5fdGVtcF9jXzFbMl0iLCAic19zbWVhbl90ZW1wX2NfMVszXSIsICAgICAgICAgInNfc21lYW5fY3VycmVudF9odW1pZGl0eV8xWzFdIiwgICJzX3NtZWFuX2N1cnJlbnRfaHVtaWRpdHlfMVsyXSIsICAic19zbWVhbl9jdXJyZW50X2h1bWlkaXR5XzFbM10iLCAgInNfc3BvcF9kZW5zaXR5X2ttMl8xWzFdIiwgICAgICAgCiMgICJzX3Nwb3BfZGVuc2l0eV9rbTJfMVsyXSIsICAgICAgICAic19zcG9wX2RlbnNpdHlfa20yXzFbM10iLCAgICAgICAgInNfc2J1aWxkaW5nX2NvdmVyYWdlX3BjdF8xWzFdIiwgICJzX3NidWlsZGluZ19jb3ZlcmFnZV9wY3RfMVsyXSIsIAojICAic19zYnVpbGRpbmdfY292ZXJhZ2VfcGN0XzFbM10iLCAgInNfc2Rpc3RfdG9fcm9hZF9tXzFbMV0iLCAic19zZGlzdF90b19yb2FkX21fMVsyXSIsICJzX3NkaXN0X3RvX3JvYWRfbV8xWzNdIikpCiMgCiMgcG93ZXJzY2FsZV9wbG90X2RlbnMobTIsIHZhcmlhYmxlID0gYygiYl9JbnRlcmNlcHQiLCAiYl9zaW5fZG95MSIsICJiX2Nvc19kb3kxIiwgImJfc2luX2RveTIiLCAiYl9jb3NfZG95MiIsICJiX3Npbl9kb3kzIiwgImJfY29zX2RveTMiLCAiYnNfc21lYW5fdGVtcF9jXzEiLCAiYnNfc21lYW5fY3VycmVudF9odW1pZGl0eV8xIiwgImJzX3Nwb3BfZGVuc2l0eV9rbTJfMSIsICJic19zYnVpbGRpbmdfY292ZXJhZ2VfcGN0XzEiLCAiYnNfc2Rpc3RfdG9fcm9hZF9tXzEiLCAgICAgICAgIAojICJzZHNfc21lYW5fdGVtcF9jXzEiLCAic2RzX3NtZWFuX2N1cnJlbnRfaHVtaWRpdHlfMSIsICJzZHNfc3BvcF9kZW5zaXR5X2ttMl8xIiwgInNkc19zYnVpbGRpbmdfY292ZXJhZ2VfcGN0XzEiLCAKIyAic2RzX3NkaXN0X3RvX3JvYWRfbV8xIiwgInNkZ3BfZ3B4eSIsICJsc2NhbGVfZ3B4eSIsICJzaWdtYSIsICJJbnRlcmNlcHQiLCAic19zbWVhbl90ZW1wX2NfMVsxXSIsICJzX3NtZWFuX3RlbXBfY18xWzJdIiwgInNfc21lYW5fdGVtcF9jXzFbM10iLCAgICAgICAgICJzX3NtZWFuX2N1cnJlbnRfaHVtaWRpdHlfMVsxXSIsICAic19zbWVhbl9jdXJyZW50X2h1bWlkaXR5XzFbMl0iLCAgInNfc21lYW5fY3VycmVudF9odW1pZGl0eV8xWzNdIiwgICJzX3Nwb3BfZGVuc2l0eV9rbTJfMVsxXSIsICAgICAgIAojICAic19zcG9wX2RlbnNpdHlfa20yXzFbMl0iLCAgICAgICAgInNfc3BvcF9kZW5zaXR5X2ttMl8xWzNdIiwgICAgICAgICJzX3NidWlsZGluZ19jb3ZlcmFnZV9wY3RfMVsxXSIsICAic19zYnVpbGRpbmdfY292ZXJhZ2VfcGN0XzFbMl0iLCAKIyAgInNfc2J1aWxkaW5nX2NvdmVyYWdlX3BjdF8xWzNdIiwgICJzX3NkaXN0X3RvX3JvYWRfbV8xWzFdIiwgInNfc2Rpc3RfdG9fcm9hZF9tXzFbMl0iLCAic19zZGlzdF90b19yb2FkX21fMVszXSIpKQpgYGAKClByZWRpY3Rpb24gbWF0cml4IGZvciBtMgoKYGBge3J9CgpuZF9tMiA8LSBuZF9tMSAlPiUKICAKCgpgYGAKCgoKCkdldCBwcmVkaWN0aW9ucyBmb3IgbW9kZWwgMgoKYGBge3J9Cm0yX3N1bSA8LSBnZXRfc3RfcHJlZGljdGlvbnMobW9kZWwgPSBtMiwgbmQ9bmRfbTIsIG1vZGVsX2lkID0gIm0yIiwgb3V0X2RpciA9ICJlcHJlZF9jaHVua3NfbTIiLCBuZHJhd3MgPSAxMDAwLCBjaHVua19zaXplID0gMTAwMDAsIHdob19wbTI1X2xpbWl0ID0gMjUpCgpgYGAKClBsb3QgcHJlZGljdGlvbnMgZm9yIG1vZGVsIDIKCmBgYHtyfQptMl9wbG90cyA8LSBwbG90X3N0X3ByZWRpY3Rpb25zKHN1bW1hcnlfZGYgPSBtMl9zdW0sIGNsdXN0ZXJfc2YgPSBzY2FsZV83Ml9jbHVzdGVycykKCm0yX3Bsb3RzICU+JSBtYXAocGxvdCkKCmBgYAoKClNhbXBsZSBjb29yZGluYXRlcywgYW5kIHBsb3Qgb3ZlciB0aW1lCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQoKI3NhbXBsZSBjb29yZGluYXRlIHBvaW50cwojd2lsbCBzb3J0IG91dCB0aGUgc21hbGwgbnVtYmVyIGxhdGVyCnNldC5zZWVkKDEyMykKc2FtcGxlZF9wb2ludHNfbTIgPC0gYXFfbW9kZWxfZGF0YSAlPiUKICBzYW1wbGVfbig1MCkgJT4lCiAgc2VsZWN0KHgsIHksIG1lYW5fdGVtcF9jLCBtZWFuX2N1cnJlbnRfaHVtaWRpdHksIHBvcF9kZW5zaXR5X2ttMiwgYnVpbGRpbmdfY292ZXJhZ2VfcGN0LCBkaXN0X3RvX3JvYWRfbSwgZ3JpZF9pZCwgbG9nX3BtMl81KQoKI2dlbmVyYXRlIERPWSAw4oCTMzY1IGFuZCBjYWxjdWxhdGUgRm91cmllciB0ZXJtcwpkb3lfZ3JpZCA8LSB0aWJibGUoZG95ID0gc2VxKDAsMzY1LCBieT0xKSkgJT4lCiAgbXV0YXRlKAogICAgc2luX2RveTEgPSBzaW4oMiAqIHBpICogZG95IC8gMzY1KSwKICAgIGNvc19kb3kxID0gY29zKDIgKiBwaSAqIGRveSAvIDM2NSksCiAgICBzaW5fZG95MiA9IHNpbig0ICogcGkgKiBkb3kgLyAzNjUpLAogICAgY29zX2RveTIgPSBjb3MoNCAqIHBpICogZG95IC8gMzY1KSwKICAgIHNpbl9kb3kzID0gc2luKDYgKiBwaSAqIGRveSAvIDM2NSksCiAgICBjb3NfZG95MyA9IGNvcyg2ICogcGkgKiBkb3kgLyAzNjUpCiAgKQoKI2V4cGFuZCBncmlkIGFjcm9zcyBzYW1wbGVkIGxvY2F0aW9ucwpwcmVkaWN0aW9uX2RmX20yIDwtIHNhbXBsZWRfcG9pbnRzX20yICU+JQogIGNyb3NzaW5nKGRveV9ncmlkKQoKI2FkZCBwcmVkaWN0aW9ucwpwcmVkc19tMiA8LSBhZGRfZXByZWRfZHJhd3Mob2JqZWN0ID0gbTIsIG5ld2RhdGEgPSBwcmVkaWN0aW9uX2RmX20yLCBuZHJhd3MgPSAxMDApCgojb2JzZXJ2ZWQgZGF0YSBmb3IgcGxvdHRpbmcKb2JzZXJ2ZWRfZGF0YSA8LSBhcV9tb2RlbF9kYXRhICU+JQogIHNlbGVjdChtZWFuX2RveSwgbG9nX3BtMl81KSAlPiUKICBtdXRhdGUobWVhbl9kb3kgPSByb3VuZChtZWFuX2RveSkpCgojcGxvdApwcmVkc19tMiAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ2dwbG90KGFlcyh4PWRveSkpICsKICBzdGF0X2xpbmVyaWJib24oYWVzKHk9LmVwcmVkKSwgLndpZHRoPTAuOTUpICsgCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IG9ic2VydmVkX2RhdGEsIGFlcyh4ID0gbWVhbl9kb3ksIHkgPSBsb2dfcG0yXzUpLAogICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLCBhbHBoYSA9IDAuNiwgd2lkdGggPSAwLjUsIGhlaWdodCA9IDAuMCwgc2l6ZSA9IDEuMikgKwogIHNjYWxlX2ZpbGxfYnJld2VyKCkgKwogIGxhYnModGl0bGUgPSAiTW9kZWwtZXN0aW1hdGVkIGxvZyhQTTIuNSkgd2l0aCBlbXBpcmljYWwgbWVhc3VyZW1lbnRzIiwKICAgICAgIHN1YnRpdGxlID0gIk1vZGVsIHByZWRpY3Rpb25zIHdpdGggOTUlIENySSBhbmQgb2JzZXJ2ZWQgZGF0YSBwb2ludHMiLAogICAgICAgeCA9ICJEYXkgb2YgeWVhciIsCiAgICAgICB5ID0gImxvZyhQTTIuNSkiLAogICAgICAgY2FwdGlvbiA9ICJNb2RlbGxlZCBlc3RpbWF0ZXMgcmVzdHJpY3RlZCB0byB3aXRoaW4gY2x1c3RlcnMiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCgoKYGBgCgoKQ29tcGFyZSBtb2RlbHMsIHVzaW5nIExPTyBDVgoKYGBge3J9CgpsaWJyYXJ5KGxvbykKCmxvb19tMSA8LSBsb28obTEpCmxvb19tMiA8LSBsb28obTIpCgpsb29fY29tcGFyZShsb29fbTEsIGxvb19tMikKYGBgCgoKIyMgUE0xMAoKTm93IHRvIG1vZGVsIFBNMTAgZGF0YQoKCgpUT0RPCgogLSBPVEhFUiBPVVRDT01FIE1FQVNVUkVTIChQTTEsIFBNMTAsIE5PKQogLSBDQU4gV0UgTElOSyBUTyBUSEUgU1RBVElDIEFRIE1PTklUT1JTIChPVVRET09SUykKIC0gUFJJT1JTCiAtIE9USEVSIENPVkFSSUFURVMKIC0gQ09NUEFSRSBNT0RFTFMgV0lUSCBESUZGRVJFTlQgQkFTSVMgRlVOQ1RJT05TIEZPUiBHUCBBTkQgU1BMSU5FUwogLSBFWENFRURBTkNFUwogLSBMSU5LIFRPIE1UQiBJTkZFQ1RJT04gUFJFVkFMRU5DRSBEQVRBLCBBTkQgTU9ERUwK